package com.example.train_operate.hander;

import cn.hutool.core.lang.Opt;
import com.example.train_operate.constants.MessageTypeConstants;
import com.example.train_operate.dto.UserDTO;
import com.example.train_operate.service.OnlineUserService;
import com.example.train_operate.ws.manager.RoomManager;
import com.example.train_operate.ws.model.OnlineUser;
import com.example.train_operate.ws.model.SignalMessage;
import com.example.train_operate.ws.model.UserSession;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.util.Signal;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.annotation.Resource;

import java.io.EOFException;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.RecursiveTask;
import java.util.stream.Collectors;

import static com.example.train_operate.constants.MessageTypeConstants.*;

/**
 * @author 21609
 * WebRTC 负责处理WebSocket消息
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class SignalingHandler extends TextWebSocketHandler {

    @Resource
    private RoomManager roomManager;

    private final ObjectMapper objectMapper = new ObjectMapper();

    private final OnlineUserService  onlineUserService;

    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        log.info("WebSocket连接已建立: {}", session.getId());
    }

    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception{
        try{
            String payload = message.getPayload();
            SignalMessage signalMessage = objectMapper.readValue(payload, SignalMessage.class);
            log.info("接收到WebSocket消息: type={}, roomId={}, senderId={}",
                    signalMessage.getType(), signalMessage.getRoomId(), signalMessage.getSenderId());
            // 根据消息类型分别处理
            switch(signalMessage.getType()){
                case "login":
                    handleLogin(session, signalMessage);
                    break;
                case "call":
                    handleCall(session, signalMessage);
                    break;
                case "call_answer":
                    handleCallAnswer(session, signalMessage);
                    break;
                case "call_reject":
                    handleCallReject(session, signalMessage);
                    break;
                case "hangup":
                    handleHangup(session, signalMessage);
                    break;
                case "join":
                    handleJoin(session, signalMessage);
                    break;
                case "offer":
                case "answer":
                case "ice_candidate":
                case "candidate":
                case "peer_ready":
                    handleWebRTCSignal(session, signalMessage);
                    break;
                case "leave":
                    handleLeave(signalMessage);
                    break;
                case "annotation":
                    handleAnnotation(signalMessage);
                    break;
                default:
                    log.warn("Unknown message type: {}", signalMessage.getType());
                    sendError(session, "UNKNOWN_TYPE", "Unknown message type: " + signalMessage.getType());
            }
        } catch (Exception e) {
            log.error("错误处理消息", e);
            sendError(session, PROCESSING_ERROR, "错误处理消息：" + e.getMessage());
        }
    }

    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        log.info("WebSocket连接关闭:{},status:{}", session.getId(), status);
        // 用户断开连接时，从在线列表中移除
        Optional<OnlineUser> onlineUser = onlineUserService.getBySessionId(session.getId());
        if (onlineUser.isPresent()){
            onlineUserService.userOffline(session.getId());
            // 广播在线用户列表更新
            broadcastOnlineUsers();
        }
        // 用户断开连接时，从房间中移除
        UserSession userSession = roomManager.getUserSessionBySessionId(session.getId());
        if (userSession != null){
            String roomId = userSession.getRoomId();
            String userId = userSession.getUserId();
            //通知房间内其他用户
            SignalMessage leftMessage = SignalMessage.createUserLeft(roomId, userId);
            broadcastToRoom(roomId, leftMessage, userId);
            //从房间中移除用户
            roomManager.leaveRoomBySessionId(session.getId());
        }
    }

    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception{
        // EOFException 是正常的连接关闭，不需要记录完整堆栈
        if(exception instanceof EOFException) {
            log.debug("WebSocket连接意外关闭：{}", session.getId());
        } else {
            log.error("WebSocket传输错误：{}", session.getId(), exception);
        }
        roomManager.leaveRoomBySessionId(session.getId());
    }

    /**
     * 处理用户登录
     */
    private void handleLogin(WebSocketSession session, SignalMessage message) throws IOException{
        Map<String, Object> data = message.getData();
        if (data == null){
            sendError(session, INVALID_REQUEST, "登录信息为空");
            return;
        }
        String userId = (String) data.get("userId");
        String userName = (String) data.get("username");
        String displayName = (String) data.get("displayName");
        String deviceType = (String) data.get("deviceType");
        if (userId == null || userName == null || deviceType == null){
            sendError(session, INVALID_REQUEST, "登录信息不完整");
            return;
        }
        //用户上线
        onlineUserService.userOnline(userId, userName, displayName, deviceType, session);
        //发送登录成功消息
        SignalMessage loginSuccessMessage = new SignalMessage();
        loginSuccessMessage.setType("login_success");
        Map<String, Object> loginData = new HashMap<>();
        loginData.put("userId", userId);
        loginData.put("message", "登录成功");
        loginSuccessMessage.setData(loginData);
        sendMessage(session, loginSuccessMessage);
        // 广播在线用户列表给所有用户
        broadcastOnlineUsers();
    }

    /**
     * 处理呼叫请求
     */
    public void handleCall(WebSocketSession session, SignalMessage message) throws IOException{
        Map<String, Object> data = message.getData();
        if(data == null){
            sendError(session, INVALID_REQUEST, "呼叫信息为空");
            return;
        }
        String fromUserId = (String) data.get("from");
        String toUserId = (String) data.get("to");
        String callerName = (String) data.get("callerName");
        if(fromUserId == null || toUserId == null){
            sendError(session, INVALID_REQUEST, "呼叫信息不完整");
            return;
        }
        // 检查目标用户是否在线
        Optional<OnlineUser> targetUser = onlineUserService.getByUserId(toUserId);
        if(!targetUser.isPresent()){
            sendError(session, USER_OFFLINE, "对方已下线");
            return;
        }
        // 检查目标用户是否忙碌
        String targetStatus = onlineUserService.getCallStatus(toUserId);
        if(!IDLE.equals(targetStatus)){
            sendError(session, USER_BUSY, "对方正在通话");
            return;
        }
        // 更新呼叫状态
        onlineUserService.setCallStatus(fromUserId, CALLING);
        onlineUserService.setCallStatus(toUserId, CALLING);
        log.info("用户{}打电话给用户{}", fromUserId, toUserId);
        // 发送来电通知给目标用户
        SignalMessage incomingCallMessage = new SignalMessage();
        incomingCallMessage.setType("incoming_call");
        Map<String, Object> callData = new HashMap<>();
        callData.put("from", fromUserId);
        callData.put("callerName", callerName != null ? callerName : fromUserId);
        incomingCallMessage.setData(callData);
        sendMessage(targetUser.get().getSession(), incomingCallMessage);
    }

    /**
     * 处理接听呼叫
     */
    private void handleCallAnswer(WebSocketSession session, SignalMessage message) throws IOException {
        Map<String, Object> data = message.getData();
        if (data == null) {
            sendError(session, INVALID_REQUEST, "接听信息为空");
            return;
        }
        String fromUserId = (String) data.get("from");
        String toUserId = (String) data.get("to");
        if(fromUserId == null || toUserId == null){
            sendError(session, INVALID_REQUEST, "消息不完整");
            return;
        }
        // 检查呼叫者是否在线
        Optional<OnlineUser> callerUser = onlineUserService.getByUserId(toUserId);
        if(!callerUser.isPresent()){
            sendError(session, USER_OFFLINE, "对方已下线");
            return;
        }
        // 更新呼叫状态
        onlineUserService.setCallStatus(fromUserId, IN_CALL);
        onlineUserService.setCallStatus(toUserId, IN_CALL);
        log.info("用户{}接听用户{}的呼叫", fromUserId, toUserId);
        // 发送接听通知给呼叫者
        SignalMessage callAcceptedMessage = new SignalMessage();
        callAcceptedMessage.setType("call_accepted");
        Map<String, Object> acceptData = new HashMap<>();
        acceptData.put("from", fromUserId);
        callAcceptedMessage.setData(acceptData);
        sendMessage(callerUser.get().getSession(), callAcceptedMessage);
    }

    /**
     * 处理拒绝呼叫
     */
    private void handleCallReject(WebSocketSession session, SignalMessage message) throws IOException{
        Map<String, Object> data = message.getData();
        if(data == null){
            sendError(session, INVALID_REQUEST, "拒绝信息为空");
            return;
        }
        String fromUserId = String.valueOf(data.get("from"));
        String toUserId = String.valueOf(data.get("to"));
        String reason = String.valueOf(data.get("reason"));
        if(fromUserId == null || toUserId == null){
            sendError(session, INVALID_REQUEST, "消息不完整");
            return;
        }
        // 检查呼叫者是否在线
        Optional<OnlineUser> callerUser = onlineUserService.getByUserId(toUserId);
        if(!callerUser.isPresent()){
            // 呼叫者已下线，无需处理
            return;
        }
        // 恢复呼叫状态
        onlineUserService.setCallStatus(fromUserId, IDLE);
        onlineUserService.setCallStatus(toUserId, IDLE);
        // 发送拒绝通知给呼叫者
        SignalMessage callRejectedMessage = new SignalMessage();
        callRejectedMessage.setType("call_rejected");
        Map<String, Object> rejectedData = new HashMap<>();
        rejectedData.put("from", fromUserId);
        rejectedData.put("reason", reason != null ? reason : "rejected");
        callRejectedMessage.setData(rejectedData);
        sendMessage(callerUser.get().getSession(), callRejectedMessage);
    }

    /**
     * 处理挂断
     */
    private void handleHangup(WebSocketSession session, SignalMessage message) throws IOException{
        Map<String, Object> data = message.getData();
        if(data == null){
            sendError(session, INVALID_REQUEST, "挂断信息为空");
            return;
        }
        String fromUserId = String.valueOf(data.get("from"));
        String toUserId = String.valueOf(data.get("to"));
        if(fromUserId == null || toUserId == null){
            sendError(session, INVALID_REQUEST, "消息不完整");
            return;
        }
        // 恢复呼叫状态
        onlineUserService.setCallStatus(fromUserId, IDLE);
        onlineUserService.setCallStatus(toUserId, IDLE);
        // 检查对方是否在线
        Optional<OnlineUser> otherUser = onlineUserService.getByUserId(toUserId);
        if (otherUser.isPresent()){
            // 发送挂断通知给对方
            SignalMessage hangupMessage = new SignalMessage();
            hangupMessage.setType("hangup");
            Map<String, Object> hangupData = new HashMap<>();
            hangupData.put("from", fromUserId);
            hangupMessage.setData(hangupData);
            sendMessage(otherUser.get().getSession(), hangupMessage);
        }
    }

    /**
     * 广播在线用户列表给所有用户
     */
    private void broadcastOnlineUsers() throws IOException{
        List<OnlineUser> onlineUsers = onlineUserService.getAllOnlineUsers();
        List<Map<String, Object>> userList = onlineUsers.stream()
                .map(user -> {
                    Map<String, Object> userData = new HashMap<>();
                    userData.put("userId", user.getUserId());
                    userData.put("userName", user.getUserName());
                    userData.put("displayName", user.getDisplayName());
                    userData.put("deviceType", user.getDeviceType());
                    userData.put("callStatus", user.getCallStatus());
                    return userData;
                })
                .collect(Collectors.toList());
        SignalMessage onlineUsersMessage = new SignalMessage();
        onlineUsersMessage.setType("online_users");
        Map<String, Object> data = new HashMap<>();
        data.put("users", userList);
        onlineUsersMessage.setData(data);
        // 发送给所有在线用户
        for(OnlineUser user : onlineUsers){
            if(user.getSession() != null && user.getSession().isOpen()){
                sendMessage(user.getSession(), onlineUsersMessage);
            }
        }
    }


    /**
     * 加入房间消息
     */
    private void handleJoin(WebSocketSession session, SignalMessage message) throws IOException {
        String roomId = message.getRoomId();
        String userId = message.getSenderId();
        if(roomId == null || userId == null){
            sendError(session, INVALID_REQUEST, "消息不完整");
            return;
        }
        //获取客户端类型
        String clientType = "unknown";
        if(message.getData() != null && message.getData().containsKey("clientType")) {
            clientType = (String) message.getData().get("clientType");
        }
        //获取当前房间用户（加入前）
        List<String> existingUsers = roomManager.getRoomUsers(roomId);
        boolean isInitiator = existingUsers.isEmpty();
        //创建用户会话
        UserSession userSession = new UserSession(userId, roomId, session, clientType);
        //加入房间
        boolean joined = roomManager.joinRoom(roomId, userSession);
        if(!joined) {
            sendError(session, ROOM_FULL, "房间已满");
            return;
        }
        // 发送加入成功消息给当前用户
        List<String> participants = roomManager.getRoomUsers(roomId);
        SignalMessage joinedMessage = SignalMessage.createJoined(roomId, userId, participants, isInitiator);
        sendMessage(session, joinedMessage);
        // 通知房间内其他用户有新用户加入
        if(!isInitiator){
            SignalMessage newUserMessage = new SignalMessage();
            newUserMessage.setType("new_user");
            newUserMessage.setRoomId(roomId);
            newUserMessage.setSenderId(userId);
            newUserMessage.setData(Map.of(
                    "userId", userId,
                    "clientType", clientType
            ));
            broadcastToRoom(roomId, newUserMessage, userId);
        }
    }

    /**
     * 处理WebRTC信令（offer, answer, ice_candidate) - 呼叫系统专用
     */
    private void handleWebRTCSignal(WebSocketSession session, SignalMessage message) throws IOException{
        // 从消息data中获取目标用户ID
        Map<String, Object> data = message.getData();
        String toUserId = data != null ? (String) data.get("toUserId") : null;
        if(toUserId == null){
            // 尝试从targetId获取
            toUserId = message.getTargetId();
        }
        if(toUserId == null){
            // 如果还是没有toUserId,尝试从roomId推断（兼容旧版本）
            String roomId = message.getRoomId();
            String senderId = message.getSenderId();
            if(roomId != null && senderId != null){
                // roomId格式：room_userId1_userId2
                String[] parts = roomId.split("_");
                if(parts.length == 3){
                    // 找出不是发送者的用户id
                    toUserId = parts[1].equals(senderId) ? parts[2] : parts[1];
                }
            }
        }
        if(toUserId == null){
            sendError(session, INVALID_REQUEST, "目标用户ID未指定");
            return;
        }
        // 查找目标用户
        Optional<OnlineUser> targetUser = onlineUserService.getByUserId(toUserId);
        if(!targetUser.isPresent() || targetUser.get().getSession() == null){
            sendError(session, USER_OFFLINE, "对方已下线");
            return;
        }
        // 转发WebRTC信令给目标用户
        sendMessage(targetUser.get().getSession(), message);
    }

    /**
     * 离开房间
     */
    private void handleLeave(SignalMessage message) throws IOException {
        String userId = message.getSenderId();
        String roomId = message.getRoomId();
        if(userId == null){
            return;
        }
        //通知房间内其他用户
        SignalMessage leftMessage = SignalMessage.createUserLeft(roomId, userId);
        broadcastToRoom(roomId, leftMessage, userId);
        //从房间中移除用户
        roomManager.leaveRoom(userId);
    }

    /**
     * 处理标注消息
     */
    private void handleAnnotation(SignalMessage message) throws IOException {
        String roomId = message.getRoomId();
        String senderId = message.getSenderId();
        String targetId = message.getTargetId();
        // 如果有targetId,通过onlineUserService查找用户会话（与WebRTC信令一致）
        if(targetId != null && !targetId.isEmpty()){
            Optional<OnlineUser> targetUser = onlineUserService.getByUserId(targetId);
            if(targetUser.isPresent() && targetUser.get().getSession() != null){
                sendMessage(targetUser.get().getSession(), message);
            } else {
                // 目标用户不存在或离线，尝试通过房间广播
                tryRoomBroadcast(roomId, message, senderId);
            }
        } else {
            // 没有targetId,尝试通过房间广播
            tryRoomBroadcast(roomId, message, senderId);
        }
    }

    /**
     * 尝试通过房间广播消息（如果房间存在）
     */
    private void tryRoomBroadcast(String roomId, SignalMessage message, String excludeUserId) throws IOException{
        if(roomId != null && !roomId.isEmpty()){
            List<UserSession> sessions = roomManager.getRoomSessions(roomId);
            if(!sessions.isEmpty()){
                for(UserSession userSession : sessions){
                    if(!userSession.getUserId().equals(excludeUserId)){
                        sendMessage(userSession.getSession(), message);
                    }
                }
            } else {
                log.warn("房间不存在: {}", roomId);
            }
        }
    }

    /**
     * 广播消息给房间内的其他用户（除了发送者）
     */
    private void broadcastToRoom(String roomId, SignalMessage message, String excludeUserId) throws IOException {
        List<UserSession> sessions = roomManager.getRoomSessions(roomId);
        for(UserSession userSession : sessions){
            if(!userSession.getUserId().equals(excludeUserId)) {
                sendMessage(userSession.getSession(), message);
            }
        }
    }

    /**
     * 发送消息到指定会话
     */
    private void sendMessage(WebSocketSession session, SignalMessage message) throws IOException {
        if (session.isOpen()) {
            // 使用synchronized确保同一时间只有一个线程向同一个session发送消息
            synchronized (session) {
                if (session.isOpen()) {  // 双重检查，确保在获取锁后session仍然是打开的
                    String json = objectMapper.writeValueAsString(message);
                    session.sendMessage(new TextMessage(json));
                }
            }
        }
    }

    /**
     * 发送错误消息
     */
    private void sendError(WebSocketSession session, String code, String errorMessage) throws IOException {
        SignalMessage error = SignalMessage.createError(code, errorMessage);
        sendMessage(session, error);
    }


}
